using System;
using System.Net;
using CodePlex.TfsLibrary.ObjectModel;
using CodePlex.TfsLibrary.RepositoryWebSvc;
using Xunit;

namespace CodePlex.TfsLibrary.ClientEngine
{
    public class TfsEngineLogTest
    {
        public class Log
        {
            [Fact]
            public void NullDelegateThrows()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();

                Assert.Throws<ArgumentNullException>(delegate
                                                     {
                                                         engine.Log(@"C:\Foo\bar.txt", VersionSpec.First, VersionSpec.Latest, 256, null);
                                                     });
            }

            [Fact]
            public void NullLocalPathThrows()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();

                Assert.Throws<ArgumentNullException>(delegate
                                                     {
                                                         engine.Log(null, VersionSpec.First, VersionSpec.Latest, 256, delegate {});
                                                     });
            }

            [Fact]
            public void NullVersionFromThrows()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();

                Assert.Throws<ArgumentNullException>(delegate
                                                     {
                                                         engine.Log(@"C:\Foo\bar.txt", null, VersionSpec.Latest, 256, delegate {});
                                                     });
            }

            [Fact]
            public void NullVersionToThrows()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();

                Assert.Throws<ArgumentNullException>(delegate
                                                     {
                                                         engine.Log(@"C:\Foo\bar.txt", VersionSpec.Latest, null, 256, delegate {});
                                                     });
            }

            [Fact]
            public void PathNotFound()
            {
                LogItem callbackItem = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();

                engine.Log(@"C:\Foo\bar.txt", VersionSpec.First, VersionSpec.Latest, 256,
                           delegate(LogItem item,
                                    SourceItemResult result)
                           {
                               callbackItem = item;
                               callbackResult = result;
                           });

                Assert.Equal(@"C:\Foo\bar.txt", callbackItem.LocalPath);
                Assert.Null(callbackItem.History);
                Assert.Equal(SourceItemResult.E_PathNotFound, callbackResult);
            }
        }

        public class Log_CredentialFailures
        {
            [Fact]
            public void CallsBackForCredentialsOnStartup()
            {
                NetworkCredential creds = new NetworkCredential("joe", "bob");
                TestableTfsEngine engine = CreateEngine();
                engine.CredentialsCallback = delegate
                                             {
                                                 Assert.False(engine.SourceControlService.QueryLog__Called);
                                                 return creds;
                                             };

                engine.Log(@"C:\Foo\bar.txt", VersionSpec.First, VersionSpec.Latest, 1, delegate {});

                Assert.True(engine.SourceControlService.QueryLog__Called);
                Assert.Same(creds, engine.SourceControlService.QueryLog_Credentials);
            }

            static TestableTfsEngine CreateEngine()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "text");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 2, 1, SourceItemStatus.Unmodified);

                return engine;
            }

            [Fact]
            public void FailWithNoCredentialCallback()
            {
                TestableTfsEngine engine = CreateEngine();
                engine.SourceControlService.QueryLog__ThrowsCredExceptionOnce = true;

                Assert.Throws<NetworkAccessDeniedException>(delegate
                                                            {
                                                                engine.Log(@"C:\Foo\bar.txt", VersionSpec.First, VersionSpec.Latest, 1, delegate {});
                                                            });
            }

            [Fact]
            public void QueryItemsFailure()
            {
                string callbackTfsUrl = null;
                TestableTfsEngine engine = CreateEngine();
                engine.SourceControlService.QueryLog__ThrowsCredExceptionOnce = true;
                engine.CredentialsCallback = delegate(ICredentials oldCredentials,
                                                      string tfsUrl)
                                             {
                                                 callbackTfsUrl = tfsUrl;
                                                 return null;
                                             };

                try
                {
                    engine.Log(@"C:\Foo\bar.txt", VersionSpec.First, VersionSpec.Latest, 1, delegate {});
                    throw new InvalidOperationException("Expected a throw!");
                }
                catch (NetworkAccessDeniedException) {}

                Assert.Equal("http://tfsUrl", callbackTfsUrl);
            }

            [Fact]
            public void Retried()
            {
                NetworkCredential creds = new NetworkCredential("jim", "jeff");
                string callbackTfsUrl = null;
                TestableTfsEngine engine = CreateEngine();
                engine.SourceControlService.QueryLog__ThrowsCredExceptionOnce = true;
                engine.CredentialsCallback =
                    delegate(ICredentials oldCredentials,
                             string tfsUrl)
                    {
                        callbackTfsUrl = tfsUrl;
                        return creds;
                    };

                engine.Log(@"C:\Foo\bar.txt", VersionSpec.First, VersionSpec.Latest, 1, delegate {});

                Assert.Equal("http://tfsUrl", callbackTfsUrl);
                Assert.Same(creds, engine.SourceControlService.QueryLog_Credentials);
            }
        }

        public class Log_File
        {
            [Fact]
            public void CallsSourceControlService()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "text");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 2, 3, SourceItemStatus.Unmodified);

                engine.Log(@"C:\Foo\bar.txt", VersionSpec.First, VersionSpec.Latest, 256, delegate {});

                Assert.Equal("$/Foo/bar.txt", engine.SourceControlService.QueryLog_ServerPath);
                Assert.Equal("http://tfsUrl", engine.SourceControlService.QueryLog_TfsUrl);
                Assert.Equal<VersionSpec>(VersionSpec.First, engine.SourceControlService.QueryLog_VersionFrom);
                Assert.Equal<VersionSpec>(VersionSpec.Latest, engine.SourceControlService.QueryLog_VersionTo);
                Assert.Equal(256, engine.SourceControlService.QueryLog_MaxCount);
            }

            [Fact]
            public void NotInAWorkingFolder()
            {
                LogItem callbackItem = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "text");

                engine.Log(@"C:\Foo\bar.txt", VersionSpec.First, VersionSpec.Latest, 256,
                           delegate(LogItem item,
                                    SourceItemResult result)
                           {
                               callbackItem = item;
                               callbackResult = result;
                           });

                Assert.Equal(@"C:\Foo\bar.txt", callbackItem.LocalPath);
                Assert.Null(callbackItem.History);
                Assert.Equal(SourceItemResult.E_NotInAWorkingFolder, callbackResult);
            }

            [Fact]
            public void NotUnderSourceControl()
            {
                LogItem callbackItem = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "text");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);

                engine.Log(@"C:\Foo\bar.txt", VersionSpec.First, VersionSpec.Latest, 256,
                           delegate(LogItem item,
                                    SourceItemResult result)
                           {
                               callbackItem = item;
                               callbackResult = result;
                           });

                Assert.Equal(@"C:\Foo\bar.txt", callbackItem.LocalPath);
                Assert.Null(callbackItem.History);
                Assert.Equal(SourceItemResult.E_NotUnderSourceControl, callbackResult);
            }

            [Fact]
            public void ReturnsLogItems()
            {
                LogItem callbackItem = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "text");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 2, 3, SourceItemStatus.Unmodified);

                engine.Log(@"C:\Foo\bar.txt", VersionSpec.First, VersionSpec.Latest, 256,
                           delegate(LogItem item,
                                    SourceItemResult result)
                           {
                               callbackItem = item;
                               callbackResult = result;
                           });

                Assert.Same(engine.SourceControlService.QueryLog_Result, callbackItem);
                Assert.Equal(SourceItemResult.S_Ok, callbackResult);
            }
        }

        public class Log_Folder
        {
            [Fact]
            public void CallsSourceControlService()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Bar");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo/Bar", @"C:\Foo\Bar", 2, 1, SourceItemStatus.Unmodified);

                engine.Log(@"C:\Foo\Bar", VersionSpec.First, VersionSpec.Latest, 256, delegate {});

                Assert.Equal("$/Foo/Bar/", engine.SourceControlService.QueryLog_ServerPath);
                Assert.Equal("http://tfsUrl", engine.SourceControlService.QueryLog_TfsUrl);
                Assert.Equal<VersionSpec>(VersionSpec.First, engine.SourceControlService.QueryLog_VersionFrom);
                Assert.Equal<VersionSpec>(VersionSpec.Latest, engine.SourceControlService.QueryLog_VersionTo);
                Assert.Equal<RecursionType>(RecursionType.Full, engine.SourceControlService.QueryLog_RecursionType);
                Assert.Equal(256, engine.SourceControlService.QueryLog_MaxCount);
            }

            [Fact]
            public void NotUnderSourceControl()
            {
                LogItem callbackItem = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Bar");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);

                engine.Log(@"C:\Foo\Bar", VersionSpec.First, VersionSpec.Latest, 256,
                           delegate(LogItem item,
                                    SourceItemResult result)
                           {
                               callbackItem = item;
                               callbackResult = result;
                           });

                Assert.Equal(@"C:\Foo\Bar", callbackItem.LocalPath);
                Assert.Null(callbackItem.History);
                Assert.Equal(SourceItemResult.E_NotUnderSourceControl, callbackResult);
            }

            [Fact]
            public void ReturnsLogItems()
            {
                LogItem callbackItem = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Bar");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Foo/Bar", @"C:\Foo\Bar", 2, 1, SourceItemStatus.Unmodified);

                engine.Log(@"C:\Foo\Bar", VersionSpec.First, VersionSpec.Latest, 256,
                           delegate(LogItem item,
                                    SourceItemResult result)
                           {
                               callbackItem = item;
                               callbackResult = result;
                           });

                Assert.Same(engine.SourceControlService.QueryLog_Result, callbackItem);
                Assert.Equal(SourceItemResult.S_Ok, callbackResult);
            }
        }
    }
}